﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media.Media3D;
using System.Windows.Input;
using System.ComponentModel;

using Microsoft.Practices.Unity;
using Microsoft.Win32;

using ObjectEditor;
using ObjectEditor.Display3D;
using ObjectEditor.Editor;

using EQEmu.Spawns;
using EQEmu.Spawns.Display3D;
using EQEmu.Database;

namespace EQEmu.ZoneSpawnsPlugin
{
    internal class ZoneSpawnsVisual3D : ModelVisual3D
    {
    }

    public class ZoneSpawnsRibbonTabViewModel : ViewModel, IWorldModel3DProvider, IObjectEditor, IDisposable, IMessenger
    {
        public event EventHandler<ModelChangedEventArgs> ModelChanged;
        public event EventHandler<CameraChangeEventArgs> RequestCameraChange;
        public event EventHandler<ObjectSelectedEventArgs> ObjectSelected;
        public event EventHandler<MessageEventArgs> MessageSent;

        private Transform3D _transform;

        private string _zoneShortName;
        private int _zoneVersion;
        private ZoneSpawns _zoneSpawns;
        private ZoneSpawnsDisplay3D _display3d;
        private ModelVisual3D _modelVisual;
        private spawn2 _selectedSpawn;
        private double _zAdjustment;
        private bool _useDatabase;
        private IEnumerable<spawn2> _multipleSelections = null;

        private DelegateCommand _saveFileCommand;
        private DelegateCommand _openFileCommand;
        private DelegateCommand _searchZoneCommand;
        private DelegateCommand _removeSpawnCommand;
        private DelegateCommand _addSpawnCommand;
        private DelegateCommand _commitChangesCommand;

        public ZoneSpawnsRibbonTabViewModel()
        {
            ZoneVersion = 0;
            ZAdjustment = 2.0;
        }

        [Dependency]
        public EQEmuContextFactory EntitiesFactory
        {
            get;
            set;
        }

        private bool CheckDBConnection()
        {
            if (_useDatabase == false) return false;
            if (EntitiesFactory == null) return false;
            else
            {
                try
                {
                    var context = EntitiesFactory.Create();
                    var query = context.spawn2.Where(x => x.id == 0).ToList();
                    context.Dispose();
                    return true;
                }
                catch (Exception)
                {
                    return false;
                }
            }
        }

        public static string ZoneSpawnsFileFilter
        {
            get
            {
                return String.Format("ZoneSpawn Files ({1}.{0})|*{1}.{0}|All Files (*.*)|*.*","xml","zonespawns");
            }
        }

        public DelegateCommand CommitChangesCommand
        {
            get
            {
                if (_commitChangesCommand == null)
                {
                    _commitChangesCommand = new DelegateCommand(
                        x =>
                        {
                            var db = _zoneSpawns as ZoneSpawnsDatabase;

                            var result = 
                                System.Windows.MessageBox.Show("Commit changes to database?", "Commit changes", System.Windows.MessageBoxButton.YesNo);
                            if (result == System.Windows.MessageBoxResult.Yes)
                            {
                                if (db != null)
                                {
                                    try
                                    {
                                        db.CommitChanges();
                                    }
                                    catch (Exception e)
                                    {
                                        System.Windows.MessageBox.Show("Failed committing changes:" + e.Message + "," + e.StackTrace);
                                    }
                                }
                            }
                        },
                        y =>
                        {
                            return _zoneSpawns != null && (_zoneSpawns as ZoneSpawnsDatabase) != null;
                        });
                }
                return _commitChangesCommand;
            }
        }

        public DelegateCommand CreateSpawnCommand
        {
            get
            {
                if (_addSpawnCommand == null)
                {
                    _addSpawnCommand = new DelegateCommand(
                        x =>
                        {
                            var sp = _zoneSpawns.CreateSpawn();
                            _zoneSpawns.AddSpawn(sp);
                            SelectedSpawn = sp;
                            _multipleSelections = null;
                        },
                        y =>
                        {
                            return _zoneSpawns != null;
                        });
                }
                return _addSpawnCommand;
            }
        }

        public DelegateCommand RemoveSpawnCommand
        {
            get
            {
                if (_removeSpawnCommand == null)
                {
                    _removeSpawnCommand = new DelegateCommand(
                        x =>
                        {
                            if (_selectedSpawn != null)
                            {
                                _zoneSpawns.RemoveSpawn(_selectedSpawn);
                                if (_multipleSelections != null)
                                {
                                    foreach (var sp in _multipleSelections.Where(y => y != _selectedSpawn))
                                    {
                                        _zoneSpawns.RemoveSpawn(sp);
                                    }
                                }
                            }
                            SelectedSpawn = null;
                            _multipleSelections = null;
                        },
                        y =>
                        {
                            return _zoneSpawns != null && (_selectedSpawn != null || _multipleSelections != null);
                        });
                }
                return _removeSpawnCommand;
            }
        }

        public DelegateCommand SaveFileCommand
        {
            get
            {
                if (_saveFileCommand == null)
                {
                    _saveFileCommand = new DelegateCommand(
                        x =>
                        {
                            var fd = new SaveFileDialog();
                            fd.FileName = ZoneShortName + "." + ZoneVersion + ".zonespawns.xml";
                            fd.Filter = ZoneSpawnsFileFilter;
                            if ((bool)fd.ShowDialog())
                            {
                                _zoneSpawns.SaveToFile(fd.FileName);
                            }
                        },
                        y =>
                        {
                            return _zoneSpawns != null;
                        });
                }
                return _saveFileCommand;
            }
        }

        public DelegateCommand OpenFileCommand
        {
            get
            {
                if (_openFileCommand == null)
                {
                    _openFileCommand = new DelegateCommand(
                        x =>
                        {
                            var od = new OpenFileDialog();
                            od.Filter = ZoneSpawnsFileFilter;
                            if ((bool)od.ShowDialog())
                            {
                                if (CheckDBConnection())
                                {
                                    try
                                    {
                                        ZoneSpawns = EQEmu.Spawns.ZoneSpawnsDatabase.LoadFromFile(od.FileName, EntitiesFactory);
                                    }
                                    catch (ArgumentException e)
                                    {
                                        System.Windows.MessageBox.Show("Could not load file:" + e.Message + Environment.NewLine + e.StackTrace);
                                    }
                                }
                                else
                                {
                                    ZoneSpawns = EQEmu.Spawns.ZoneSpawns.LoadFromFile(od.FileName);
                                }
                            }
                        },
                        y =>
                        {
                            return true;
                        });
                }
                return _openFileCommand;
            }
        }

        public DelegateCommand SearchZoneCommand
        {
            get
            {
                if (_searchZoneCommand == null)
                {
                    _searchZoneCommand = new DelegateCommand(
                        x =>
                        {
                            LoadZoneData();
                        },
                        y =>
                        {
                            return true;
                        });
                }
                return _searchZoneCommand;
            }
        }

        private ZoneSpawns ZoneSpawns
        {
            get { return _zoneSpawns; }
            set
            {
                if (value != null)
                {
                    if (_zoneSpawns != null)
                    {
                        var disposer = _zoneSpawns as IDisposable;
                        if (disposer != null) disposer.Dispose();
                    }

                    _zoneSpawns = value;
                    SaveFileCommand.RaiseCanExecuteChanged();
                    if (_display3d != null)
                    {
                        _display3d.Dispose();
                    }
                    _display3d = new ZoneSpawnsDisplay3D(_zoneSpawns);
                    Model3D = new ZoneSpawnsVisual3D()
                    {
                        Content = _display3d.Model,
                        Transform = Transform3D
                    };

                    ZoneShortName = value.ZoneShortName;
                    ZoneVersion = value.ZoneVersion;
                }
                CreateSpawnCommand.RaiseCanExecuteChanged();
                CommitChangesCommand.RaiseCanExecuteChanged();
            }
        }

        public bool UseDatabase
        {
            get { return _useDatabase; }
            set
            {
                _useDatabase = value;
                OnPropertyChanged("UseDatabase");
            }
        }

        public int ZoneVersion
        {
            get { return _zoneVersion; }
            set
            {
                _zoneVersion = value;
                OnPropertyChanged("ZoneVersion");
            }
        }

        private void LoadZoneData()
        {
            if (_zoneSpawns != null && _zoneSpawns.HasModifications)
            {
                var result =
                    System.Windows.MessageBox.Show("Discard all changes?", "Discard channges", System.Windows.MessageBoxButton.YesNo);
                if (result == System.Windows.MessageBoxResult.No) return;
            }

            if (UseDatabase)
            {
                try
                {
                    ZoneSpawns = new ZoneSpawnsDatabase(ZoneShortName, EntitiesFactory, ZoneVersion);
                }
                catch (Exception e)
                {
                    System.Windows.MessageBox.Show("Could not created object:" + e.Message + Environment.NewLine + e.StackTrace);
                }
            }
            else
            {
                ZoneSpawns = new ZoneSpawns(ZoneShortName, ZoneVersion);
            }
        }

        public string ZoneShortName
        {
            get { return _zoneShortName; }
            set
            {
                _zoneShortName = value;
                OnPropertyChanged("ZoneShortName");
            }
        }

        public double ZAdjustment
        {
            get { return _zAdjustment; }
            set
            {
                _zAdjustment = value;
                OnPropertyChanged("ZAdjustment");
            }
        }

        public spawn2 SelectedSpawn
        {
            get { return _selectedSpawn; }
            set
            {
                _selectedSpawn = value;

                OnPropertyChanged("SelectedSpawn");
                if (value != null)
                {
                    var point = _multipleSelections != null && _multipleSelections.Count() > 0 ?
                        this.CenterPoint(_multipleSelections) : new Point3D(value.x,value.y,value.z);                    
                    var look = Transform3D.Transform(point);

                    OnMessageSent("GRID.SELECT", value.pathgrid);
                    OnObjectSelected(value, _multipleSelections);
                    OnCameraChange(new CameraChangeEventArgs()
                    {
                        LookAtPosition = look
                    });
                }
                else
                {
                    _multipleSelections = null;
                    OnObjectSelected(null, null);
                }
                RemoveSpawnCommand.RaiseCanExecuteChanged();
            }
        }

        [Dependency("EQEmu.Transform")]
        public Transform3D Transform3D
        {
            get { return _transform; }
            set
            {
                _transform = value;
            }
        }

        public ModelVisual3D Model3D
        {
            get { return _modelVisual; }
            private set
            {
                if (_modelVisual != null)
                {
                    _modelVisual.Children.Clear();
                }

                _modelVisual = value;
                OnPropertyChanged("Model3D");
                OnModelChanged();
            }        
        }

        protected void OnModelChanged()
        {
            var e = ModelChanged;
            if (e != null)
            {
                e(this, new ModelChangedEventArgs());
            }
        }

        protected void OnObjectSelected(INotifyPropertyChanged obj,IEnumerable<INotifyPropertyChanged> objs)
        {
            var e = ObjectSelected;
            if (e != null)
            {
                e(this, new ObjectSelectedEventArgs(obj,objs));
            }
        }

        protected void OnCameraChange(CameraChangeEventArgs change)
        {
            var e = RequestCameraChange;
            if (e != null)
            {
                e(this, change);
            }
        }

        private Point3D CenterPoint(IEnumerable<spawn2> spawns)
        {
            var points = new List<Point3D>();
            foreach (var n in spawns)
            {
                points.Add(new Point3D(n.x, n.y, n.z));
            }

            return Common.Functions.Center(points);
        }

        public void UserClicked(System.Windows.Media.Media3D.Point3D point, AppInformation info)
        {
            if (info.SelectedRibbonTab == null || (info.SelectedRibbonTab as ZoneSpawnsRibbonTab) == null) return;
            if (_zoneSpawns == null) return;

            var p = Transform3D.Transform(point);

            if ((Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt)) && SelectedSpawn != null)
            {
                var sp = SelectedSpawn;
                var center = new Point3D(SelectedSpawn.x, SelectedSpawn.y, SelectedSpawn.z);
                if (_multipleSelections != null && _multipleSelections.Count() > 1)
                {
                    center = this.CenterPoint(_multipleSelections);
                }

                double transX = p.X - center.X;
                double transY = p.Y - center.Y;
                double transZ = p.Z - center.Z + ZAdjustment;

                sp.x += (float)transX;
                sp.y += (float)transY;
                sp.z += (float)transZ;

                if (_multipleSelections != null)
                {
                    foreach (var s in _multipleSelections.Where(x => x != sp))
                    {
                        s.x += (float)transX;
                        s.y += (float)transY;
                        s.z += (float)transZ;
                    }
                }


                SelectedSpawn = sp;
            }
            else if ((Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) && SelectedSpawn != null)
            {
                //change the heading
                SelectedSpawn.heading = (float)Functions.LookAt(SelectedSpawn.x, SelectedSpawn.y, SelectedSpawn.z, p.X, p.Y, p.Z);
                if (_multipleSelections != null)
                {
                    foreach (var s in _multipleSelections)
                    {
                        s.heading = (float)Functions.LookAt(s.x, s.y, s.z, p.X, p.Y, p.Z);
                    }
                }
            }
            else if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
            {
                var sp = _zoneSpawns.CreateSpawn();
                sp.x = (float)p.X;
                sp.y = (float)p.Y;
                sp.z = (float)p.Z + (float)ZAdjustment;
                _zoneSpawns.AddSpawn(sp);

                SelectedSpawn = sp;
                _multipleSelections = null;
            }
            else
            {
                var sp = _zoneSpawns.GetNearbySpawn(p);

                if (info.World3DSelectionBox != null)
                {
                    var lst = new List<spawn2>();
                    foreach(var s in _zoneSpawns.Spawns)
                    {
                        var tp = new Point3D(s.x,s.y,s.z);
                        tp = Transform3D.Transform(tp);
                        if (info.World3DSelectionBox(tp, 5.0))
                        {
                            lst.Add(s);
                        }
                    }
                    _multipleSelections = lst;
                    if (sp == null && _multipleSelections.Count() > 0)
                    {
                        sp = _multipleSelections.ElementAt(0);
                    }
                }

                if (sp != null)
                {
                    SelectedSpawn = sp;
                }
            }
        }

        public void ConstrainView(ViewConstraints constraints)
        {
            if (_display3d == null) return;

            _display3d.UpdateView(new EQEmu.Display3D.ViewConstraints()
            {
                MaxX = constraints.MaxX,
                MaxY = constraints.MaxY,
                MaxZ = constraints.MaxZ,
                MinX = constraints.MinX,
                MinY = constraints.MinY,
                MinZ = constraints.MinZ
            });
        }

        protected virtual void Dispose(bool disposeAll)
        {
            if (_display3d != null)
            {
                _display3d.Dispose();
            }

            if (_zoneSpawns != null)
            {
                var disposer = _zoneSpawns as IDisposable;
                if (disposer != null) disposer.Dispose();
            }
        }

        public void Dispose()
        {
            Dispose(true);
        }        

        public void MessageAvailable(object sender, MessageEventArgs data)
        {
            return;
        }

        protected void OnMessageSent(string title, object data)
        {
            var e = MessageSent;
            if (e != null)
            {
                e(this, new MessageEventArgs(title, data));
            }
        }
    }
}
